package com.niiwoo.civet.account.service.local.openAccount2.service;

import com.alibaba.fastjson.JSONObject;
import com.niiwoo.civet.account.constant.AccountRegisterErrorCode;
import com.niiwoo.civet.account.dao.entity.AccountBase;
import com.niiwoo.civet.account.dao.entity.OpenAccountRecord;
import com.niiwoo.civet.account.dao.mapper.OpenAccountRecordMapperExt;
import com.niiwoo.civet.account.enums.AccountActiveEnum;
import com.niiwoo.civet.account.enums.OpenAccountMajorWay;
import com.niiwoo.civet.account.enums.OpenAccountRecordStatusEnum;
import com.niiwoo.civet.account.service.local.openAccount2.account.AccountInfoComposite;
import com.niiwoo.civet.account.service.local.openAccount2.dto.QueryUpdateRequest;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.provider.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.LocalDateTime;
import org.joda.time.Minutes;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.function.Supplier;

@Component
@Slf4j
public class OpenAccountServices implements ApplicationContextAware {

    @Autowired
    OpenAccountRecordMapperExt openAccountRecordMapperExt;
    @Autowired
    AccountInfoComposite accountInfoComposite;
    private ApplicationContext ctx;
    private Map<Class, AbstractOpenAccountService> openAccountServiceMap = new HashMap<>(0);

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (applicationContext.getParent() == null) ctx = applicationContext;
    }

    @PostConstruct
    public void init() {
        Optional.ofNullable(ctx.getBeansOfType(AbstractOpenAccountService.class)).ifPresent(m -> m.values().forEach(e -> openAccountServiceMap.put(e.getType(), e)));
    }

    public <T> AbstractOpenAccountService<T> get(Class<T> orderClass) {
        return openAccountServiceMap.get(orderClass);
    }

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public  <T> T doInTransaction(Supplier<T> s) {
        return s.get();
    }


    public OpenAccountMajorWay verifyAndGetOpenAccountWay(String orgId, String userId, UserRoleEnum role) {
        List<OpenAccountRecord> recordList = ((OpenAccountServices) AopContext.currentProxy()).doInTransaction(() -> openAccountRecordMapperExt.selectByUserIdAndOrgId(userId, orgId));
        log.info("开户前检查,用户ID={},机构ID={},用户角色={}", userId, orgId, role);
        if (recordList != null && !recordList.isEmpty()) {
            // 当前角色开户记录, 禁止[成功][审核中], 允许[处理中][审核退回][审核拒绝]
            recordList.stream()
                    .filter(r -> Objects.equals(r.getAccountRole(), role.getValue()))
                    .findFirst()
                    .ifPresent(cr -> {
                        log.info("开户前检查,用户ID={},开户记录={}", userId, JSONObject.toJSONString(cr));
                        switch (OpenAccountRecordStatusEnum.of(cr.getStatus())) {
                            // 成功
                            case SUCCESS:
                                throw new BizException(AccountRegisterErrorCode.HAS_REGISTERED_ACCOUNT);
                                // 处理中
                            case PROCESSING:
                                // 审核中
                            case AUDITING: {
                                QueryUpdateRequest request = new QueryUpdateRequest();
                                request.setUserId(userId);
                                request.setRole(cr.getAccountRole());
                                request.setOrgId(orgId);
                                OpenAccountRecordStatusEnum updateStatus = this.get(Void.class).update(request).getRecordStatus();
                                switch (updateStatus) {
                                    case SUCCESS:
                                        throw new BizException(AccountRegisterErrorCode.HAS_REGISTERED_ACCOUNT);
                                    case AUDITING:
                                        throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDITING);
                                }
                            }
                            break;
                            default: // 默认不处理
                        }
                    });
            // 其他角色开户记录, 禁止[处理中][审核中][审核退回], 允许[审核拒绝][成功]
            Optional<OpenAccountRecord> otherRecordStatusOpt = recordList.stream()
                    .filter(r -> !Objects.equals(r.getAccountRole(), role.getValue()))
                    .findFirst();
            otherRecordStatusOpt.ifPresent(or -> {
                log.info("开户前检查,用户ID={},其他角色开户记录={}", userId, JSONObject.toJSONString(or));
                switch (OpenAccountRecordStatusEnum.of(or.getStatus())) {
                    // 审核退回
                    case AUDIT_RETURN:
                        throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDIT_RETURN);
                        // 处理中
                    case PROCESSING:
                    case AUDITING: {
                        QueryUpdateRequest request = new QueryUpdateRequest();
                        request.setUserId(userId);
                        request.setRole(or.getAccountRole());
                        request.setOrgId(orgId);
                        OpenAccountRecordStatusEnum updateStatus = this.get(Void.class).update(request).getRecordStatus();
                        switch (updateStatus) {
                            case PROCESSING: {
                                int minutes = Math.min(25, Minutes.minutesBetween(new LocalDateTime(or.getLastRequestTime()), new LocalDateTime()).getMinutes());
                                String left = String.valueOf(Math.max(1, 25 - minutes));
                                throw new BizException(AccountRegisterErrorCode.HAS_UNFINISHED_OPEN_ACCOUNT_RECORD, left);
                            }
                            case AUDITING:
                                throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDITING);
                            case AUDIT_RETURN:
                                throw new BizException(AccountRegisterErrorCode.ACCOUNT_AUDIT_RETURN);
                        }
                    }
                    break;
                    default: // 默认不处理
                }
            });
            // 存在已成功记录，则使用集团账户
            if (otherRecordStatusOpt.map(r -> r.getStatus().equals(OpenAccountRecordStatusEnum.SUCCESS.getValue())).orElse(false)) {
                return OpenAccountMajorWay.GROUP_ACCOUNT_CORRELATE;
            }
        }
        // 查询是否存在待激活的账户
        AccountBase accountBase = accountInfoComposite.getAccount(userId, role,orgId);
        if (accountBase != null && accountBase.getImportUserActiva().equals(AccountActiveEnum.UNACTIVATED.getValue())) {
            return OpenAccountMajorWay.ACTIVATE;
        } else {
            return OpenAccountMajorWay.PERSON_BIND_BANK_CARD_REGISTER;
        }
    }

}
